const http = require('http')
const slice = Array.prototype.slice

class LikeExpress {
    constructor() {
        // 存放中间件列表
        this.routes = {
            // all, get, post 就是分别以app.use, app.get, app.post 存放的中间件
            all: [],
            get: [],
            post: []
        }
    }

    // 不管是 use,get,post,listen 都要先注册的函数，内部共有的，分析参数
    register(path) {
        // 中间件的第一个参数可能是回调函数，也可能是path
        const info = {}
        if (typeof path === 'string') {
            info.path = path
            // 把参数从第二个开始的后续全部取出来，放到 stack 中，slice.call 赋值为数组
            info.stack = slice.call(arguments, 1)
        } else {
            // 把参数从第一个开始的后续全部取出来，放到 stack 中，slice.call 赋值为数组
            info.path = '/'
            info.stack = slice.call(arguments, 0)
        }
        return info
    }

    use() {
        // 将 use 方法所有的参数全部放到 info 中去
        const info = this.register.apply(this, arguments)
        // 将所有的参数（中间件）放到all 这个列表中去
        this.routes.all.push(info)
    }

    get() {
        // 同上
        const info = this.register.apply(this, arguments)
        // 将所有的参数（中间件）放到 all 这个列表中去
        this.routes.get.push(info)
    }

    post() {
        // 同上
        const info = this.register.apply(this, arguments)
        // 将所有的参数（中间件）放到 all 这个列表中去
        this.routes.post.push(info)
    }

    match(method, url) {
        let stack = []
        // 浏览器自带的小图标,忽略掉
        if (url === '/favicon.ico') {
            return stack
        }

        // 获取 routes
        let curRoutes = []
        // 拼接 all 中所有的 路由
        curRoutes = curRoutes.concat(this.routes.all)
        // 然后根据 method 是什么来继续拼接
        curRoutes = curRoutes.concat(this.routes[method])

        curRoutes.forEach(routeInfo => {
            if (url.indexOf(routeInfo.path) === 0 ) {
                // 这种情况：当前的 url === '/api/get-cookie' 且 routeInfo.path === '/'
                // 当前的 url === '/api/get-cookie' 且 routeInfo.path === '/path'
                // 当前的 url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie'
                // 符合以上类似的情况，就将 url 放到 stack 中去
                stack = stack.concat(routeInfo.stack)
            }
        })

        return stack
    }

    // 核心的 next 机制
    handle(req, res, stack) {
        const next = () => {
            // 拿到第一个匹配的中间件
            const middleWare = stack.shift()
            if (middleWare) {
                // 执行中间件函数
                middleWare(req, res, next)
            }
        }
        next()
    }

    callback() {
        return (req, res) => {
            res.json = (data) => {
                // 因为要返回JSON格式，所以要设置 setHeader
                res.setHeader('Content-type', 'application/json')
                res.end(
                    JSON.stringify(data)
                )
            }
            const url = req.url
            const method = req.method.toLowerCase()
            // 通过 url 和 method 来区分哪些 参数 需要访问，哪些不需要
            // 然后将 url 和 method 传入到 match 方法中
            const resultList = this.match(method, url)
            this.handle(req, res, resultList)
        }
    }

    // 解构参数，参数个数不定
    listen(...args) {
        const server = http.createServer(this.callback())
        // 参数透传
        server.listen(...args)
    }
}

// 工厂函数
module.exports = () => {
    return new LikeExpress()
}